//
//  XTStatusListViewModel.swift
//  MyBlog
//
//  Created by victor on 16/12/19.
//  Copyright © 2016年 victor. All rights reserved.
//

import Foundation
import SDWebImage

/** 
 父类的选择

 - 如果类需要使用'KVC'，或者字典转模型框架设置对象值，类就需要继承自NSObject
 - 如果类只是包装一些代码逻辑(写了一些函数)，可以不用任何父类，好处，更加轻量级，内存消耗越小
 - 如果用 OC 写，一律都继承自NSObject 即可
 
 - 使命：负责微博的数据处理
 1.字典转模型
 2.下拉/上拉刷新数据处理
 */

/// 上拉刷新最大尝试次数
fileprivate let maxPullupTryTimes = 3

/// 微博数据列表视图模型
class XTStatusListViewModel {
    
    /// 微博视图模型数组懒加载
    lazy var statusList = [XTStatusViewModel]()
    
    /// 上拉刷新错误次数
    fileprivate var pullupErrorTimes = 0
    
    /// 加载微博列表
    ///
    /// - Parameters:
    ///   - pullUp: 是否上拉刷新标记
    ///   - completion: 完成回调(网络请求是否成功，是否刷新表格)
    func loadStatus(pullUp: Bool, completion:@escaping (_ isSuccess: Bool, _ shouldRefresh: Bool)->()) {
        
        // 判断是否是上拉刷新，同时检查刷新错误
        // 因为这个项目并没有审核通过，所以上拉刷新一般每天只能刷新100条所有，再多了就封号了，你今天就测试不了啦
        if pullUp && pullupErrorTimes > maxPullupTryTimes {
            completion(true,false)
            return
        }
        
        // 下拉刷新 since_id 取出数组中第一条微博的 id
        // 如果是 pullUp ，则是上拉刷新， since_id 直接设置为0
        // 因为 刷新的数据不同 since_id 和 max_id 不能同时有效
        let since_id = pullUp ? 0 : (statusList.first?.status.id ?? 0)
        // 上拉刷新 取出数组中的最后一条微博id
        let max_id = !pullUp ? 0 : (statusList.last?.status.id ?? 0)
        
        // 发起网络请求，加载微博数据(字典的数组)
        XTNetWorkManger.shared.statusList(since_id: since_id,max_id: max_id) { (list, isSuccess) in
            
            // 如果网络请求失败，直接执行完成回调
            if !isSuccess {
                // 直接回调返回
                completion(false, false)
                
                return
            }
            
            // 遍历字典数组，字典转 模型 => 视图模型，将视图模型添加到数组
            var array = [XTStatusViewModel]()
            
            // 遍历服务器返回的字典数组，字典转模型
            for dict in list ?? [] {
                
                // 1> 创建微博模型
                let status = XTStatus()
                
                // 2> 使用字典设置模型数值
                status.yy_modelSet(with: dict)
                
                // 3> 使用 `微博` 模型创建 `微博视图` 模型
                let viewModel = XTStatusViewModel(model: status)
                
                // 4> 添加到数组
                array.append(viewModel)
            }
            
            // 视图模型创建完成
            print("刷新到 \(array.count) 条数据 \(array)")
            
            // 拼接数据
            if pullUp {
                // 上拉刷新结束后，将结果拼接在数组的末尾
                self.statusList += array
            } else {
                // 下拉刷新，应该将结果数组拼接在数组前面
                self.statusList = array + self.statusList
            }
            
            // 判断上拉刷新的数据量
            if pullUp && array.count == 0 {
                self.pullupErrorTimes += 1
                completion(isSuccess,false)
            } else {
                // 真正有数据的回调！
//                completion(isSuccess,true)
                
                self.cacheSingleImage(list: array,finished: completion)
            }
        }
    }
    
    /// 缓存本次下载微博数据数组中的单张图像
    ///
    /// - 应该缓存完单张图像，并且修改过配图是的大小之后，再回调，才能够保证表格等比例显示单张图像！
    /// - 闭包是准备好的代码，可以当做参数传递
    ///
    /// - Parameter list: 本次下载的视图模型数组
    fileprivate func cacheSingleImage(list: [XTStatusViewModel],finished:@escaping (_ isSuccess: Bool, _ shouldRefresh: Bool)->()) {
        
        // 调度组
        let group = DispatchGroup()
        
        // 记录数据长度
        var length = 0
        
        // 遍历数组，查找微博数据中有单张图像的进行缓存
        // option + cmd + 左，折叠代码
        for viewModel in list {
            // 判断图像数量
            if viewModel.picURLs?.count != 1 {
                continue
            }
            
            // 代码执行到此，数组中有且仅有一张图片
            guard let pic = viewModel.picURLs![0].thumbnail_pic,
                let url = URL(string: pic) else {
                continue
            }
            
//             print("要缓存的 URL 是 \(url)")
            
            
            /**
                调度组最重要的目的： 监听一组任务的完成
                调度组为空，notify监听方法调用，由指定的队列调用
                
                1、调度组没有任务，直接执行notify
                2、入组多于出组，notify 永远不会执行，因为组永远不会为空
                3、出组多于入组，会崩溃
             
                -入组和出组要配对使用
            */
            
            // 3> 下载图像
            // 1) downloadImage 是 SDWebImage 的核心方法
            // 2) 图像下载完成之后，会自动保存在沙盒中，文件路径是 URL 的 md5
            // 3) 如果沙盒中已经存在缓存的图像，后续使用 SD 通过 URL 加载图像，都会加载本地沙盒地图像
            // 4) 不会发起网路请求，同时，回调方法，同样会调用！
            // 5) 方法还是同样的方法，调用还是同样的调用，不过内部不会再次发起网路请求！
            // *** 注意点：如果要缓存的图像累计很大，要找后台要接口！
            
            // 入组 
            group.enter()
            
            SDWebImageManager.shared().downloadImage(with: url, options: [], progress: nil, completed: { (image, _, _, _, _) in
                
                // 转换成二进制数据
                if let image = image,
                    let data = UIImagePNGRepresentation(image) {
                    
                    // NSData 是 length 属性
                    length += data.count
                    
                    // 图像缓存成功，更新配图视图的大小
                    viewModel.updateSingleImageSize(image: image)
                }
                
                print("缓存的图像是 \(image) 长度 \(length)")
                
                // 出组 - 放在回调的最后一句
                group.leave()
            })
            
        }
        
        // 监听调度组情况
        group.notify(queue: DispatchQueue.main) { 
            print("图像缓存完成 \(length / 1024) K")
            // 执行闭包回调
            finished(true, true)
        }
    }
}
